package com.heima.comment.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.apis.IUserClient;
import com.heima.comment.dtos.CommentLikeDto;
import com.heima.comment.dtos.CommentRepayDto;
import com.heima.comment.dtos.CommentRepayLikeDto;
import com.heima.comment.dtos.CommentRepayListDto;
import com.heima.comment.pojos.ApComment;
import com.heima.comment.pojos.ApCommentLike;
import com.heima.comment.service.CommentRepayService;
import com.heima.comment.vos.ApCommentVo;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.thread.ApThreadLocalUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CommentRepayServiceImpl implements CommentRepayService {
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private IUserClient userClient;

    @Autowired
    private GreenTextScan textScan;

    /**
     * 保存评论列表
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveCommentRepay(CommentRepayDto dto) {
        try {
            // 1.校验参数
            if (dto == null || dto.getCommentId() == null) {
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "当前评论id输入错误");
            }
            if (dto.getContent() == null || dto.getContent().length() == 0 || dto.getContent().length() > 140) {
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID, "当前评论内容输入错误");
            }
            /* Map map = textScan.greeTextScan(dto.getContent());
            if ("block".equals(map.get("suggestion"))) {
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "当前评论内容存在违规");
            }
            if ("review".equals(map.get("suggestion"))) {
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR, "当前评论内容存在不确定词汇");
            } */
            // 2.获取用户信息
            ApUser apUser = ApThreadLocalUtil.getUser();
            if (apUser == null || apUser.getId().equals(0)) {
                return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            }
            Integer userId = apUser.getId();
            String key = "USER_LOGIN" + userId;
            String userStr = redisTemplate.opsForValue().get(key);
            // redis中不存在用户信息
            if (StringUtils.isEmpty(userStr)) {
                // 远程调用userClient查询用户信息
                apUser = userClient.findUserById(Long.valueOf(userId));
                // 保存用户信息到redis
                redisTemplate.opsForValue().set(key, JSON.toJSONString(apUser), 1, TimeUnit.HOURS);
            } else {
                // redis中存在用户信息
                apUser = JSON.parseObject(userStr, ApUser.class);
            }
            // 3.封装评论信息
            ApComment apComment = new ApComment();
            apComment.setUserId(userId);
            apComment.setNickName(apUser.getName());
            apComment.setImage(apUser.getImage());
            apComment.setTargetId(String.valueOf(dto.getCommentId()));
            apComment.setContent(dto.getContent());
            apComment.setLikes(0);
            apComment.setReply(0);
            apComment.setCreatedTime(new Date());
            apComment.setUpdatedTime(new Date());
            // 4.保存到mongo
            mongoTemplate.insert(apComment);
            mongoTemplate.updateFirst(
                    Query.query(Criteria.where("id").is(dto.getCommentId())),
                    new Update().inc("reply", 1),
                    ApComment.class
            );
            // 5.返回数据
            return ResponseResult.okResult(200, "评论成功");
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 点赞评论方法
     * @param dto
     * @return
     */
    @Override
    public ResponseResult like(CommentRepayLikeDto dto) {
        // 1.校验参数
        if (dto == null || dto.getCommentRepayId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2.获取用户信息
        ApUser apUser = ApThreadLocalUtil.getUser();
        if (apUser == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        Integer apUserId = apUser.getId();
        if (apUserId == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN, "游客身份，需要登录");
        }
        // 3.查看当前评论是否存在
        ApComment apComment = mongoTemplate.findById(dto.getCommentRepayId(), ApComment.class);
        // 4.评论不存在，终止
        if (apComment == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST, "当前评论不存在");
        }
        // 5.评论存在
        // 从redis中查询当前评论是否被当前用户点赞过
        String key = "COMMENT_LIKE_" + apUser.getId();
        Boolean isLikeFlag = redisTemplate.opsForSet().isMember(key, dto.getCommentRepayId());
        // 如果评论已点赞，并且执行的是点赞操作，则终止
        if (isLikeFlag && dto.getOperation() == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH, "无法重复执行点赞");
        }
        // 如果评论已点赞，并且执行的是取消点赞操作，则删除redis和mongo中的数据，并且点赞数-1
        removeRedisAndMongo(dto, apUserId, key, isLikeFlag);
        // 如果评论没点赞，并且执行的是取消点赞操作，则终止
        if (!isLikeFlag && dto.getOperation() == 1) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NO_OPERATOR_AUTH, "无法执行取消点赞");
        }
        // 如果评论没点赞，并且执行的是点赞操作，则添加数据到redis和mongo，并且点赞数+1
        insertRedisAndMongo(dto, apUserId, key, isLikeFlag);
        apComment = mongoTemplate.findById(dto.getCommentRepayId(), ApComment.class);
        Map<String, Integer> result = new HashMap<>();
        result.put("likes", apComment.getLikes());
        return ResponseResult.okResult(result);
    }

    /**
     * 根据评论id去查询所有的评论回复
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(CommentRepayListDto dto) {
        // 1.校验参数
        if (dto == null || dto.getCommentId() == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if (dto.getSize() == null ||dto.getSize() == 0) {
            dto.setSize(10);
        }
        // 2.根据文章id查询评论列表
        List<ApComment> apCommentList = mongoTemplate.find(
                Query.query(
                        Criteria.where("targetId").is(dto.getCommentId().toString())
                                .and("createdTime").lt(dto.getMinDate())
                ).with(Sort.by(Sort.Direction.DESC, "createdTime")).limit(dto.getSize()),
                ApComment.class
        );
        if (apCommentList == null || apCommentList.size() == 0) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        // 3.如果是游客，直接返回评论列表
        Integer userId = ApThreadLocalUtil.getUser().getId();
        if (userId == 0) {
            return ResponseResult.okResult(apCommentList);
        }
        // 4.如果是登录用户，遍历列表转化成commentVo
        String key="COMMENT_LIKE_"+ userId;
        Set<String> members = redisTemplate.opsForSet().members(key);
        // 当前评论被点赞过，将operation值设为0
        List<ApCommentVo> apCommentVoList = apCommentList.stream().map(
                apComment -> {
                    ApCommentVo apCommentVo = new ApCommentVo();
                    BeanUtils.copyProperties(apComment, apCommentVo);
                    if (members != null && members.contains(apComment.getId())) {
                        apCommentVo.setOperation((short) 0);
                    }
                    return apCommentVo;
                }
        ).collect(Collectors.toList());
        return ResponseResult.okResult(apCommentVoList);
    }

    /**
     * 新增数据到redis和mongo
     * @param dto
     * @param apUserId
     * @param key
     * @param isLikeFlag
     */
    private void insertRedisAndMongo(CommentRepayLikeDto dto, Integer apUserId, String key, Boolean isLikeFlag) {
        if (!isLikeFlag && dto.getOperation() == 0) {
            // 新增redis
            redisTemplate.opsForSet().add(key, dto.getCommentRepayId());
            // 新增mongo
            ApCommentLike apCommentLikes = new ApCommentLike();
            apCommentLikes.setUserId(apUserId);
            apCommentLikes.setTargetId(dto.getCommentRepayId());
            apCommentLikes.setCreatedTime(new Date());
            mongoTemplate.insert(apCommentLikes);
            // 更新mongo点赞评论表的点赞数+1
            mongoTemplate.updateFirst(
                    Query.query(
                            Criteria.where("id").is(dto.getCommentRepayId())
                    ),
                    new Update().inc("likes", 1),
                    ApComment.class
            );
        }
    }

    /**
     * 移除数据到redis和mongo
     * @param dto
     * @param apUserId
     * @param key
     * @param isLikeFlag
     */
    private void removeRedisAndMongo(CommentRepayLikeDto dto, Integer apUserId, String key, Boolean isLikeFlag) {
        if (isLikeFlag && dto.getOperation() == 1) {
            // 删除redis
            redisTemplate.opsForSet().remove(key, dto.getCommentRepayId());
            // 删除mongo
            mongoTemplate.remove(
                    Query.query(
                            Criteria.where("userId").is(apUserId)
                                    .and("targetId").is(dto.getCommentRepayId())
                    ),
                    ApCommentLike.class
            );
            // 更新mongo点赞评论表的点赞数-1
            mongoTemplate.updateFirst(
                    Query.query(
                            Criteria.where("id").is(dto.getCommentRepayId())
                                    .and("likes").lt(0)
                    ),
                    new Update().inc("likes", -1),
                    ApComment.class
            );
        }
    }
}
